#include <QtCore>
#include <QtNetwork>
#include <stdio.h>
#include <fcntl.h>
#include "t_core.h"
#include "t_youtube_dump_lib.h"

class COutput : public QObject {
    Q_OBJECT
protected:
    qint64 f_total_bytes;
    qint64 f_written_bytes;
    T_AutoRunFPS f_fps;
public:
    explicit COutput()
        : f_total_bytes(0)
        , f_written_bytes(0)
        , f_fps(2000)
    {
    }
    bool init(qint64 a_total_bytes)
    {
        f_total_bytes = a_total_bytes;
        f_written_bytes = 0;
        int v_result;
        v_result = _setmode( _fileno( stdout ), _O_BINARY );
        Q_UNUSED(v_result);
        return (f_total_bytes > 0);
    }
    void write_bytes(const QByteArray &a_bytes)
    {
        fwrite(a_bytes.constData(), 1, a_bytes.size(), stdout);
        fflush(stdout);
        f_written_bytes += a_bytes.size();
        f_fps.set(f_written_bytes);
        double v_pct = f_written_bytes * 100.0 / f_total_bytes;
        double v_fps_value = f_fps.value();
        double v_secs_left = (f_total_bytes - f_written_bytes) / v_fps_value;
        QTextStream v_ts(stderr);
        //v_ts.setCodec(QTextCodec::codecForLocale());
        v_ts << "(3)"
                << qSetFieldWidth(8)
                << QString("(%L1 秒secs)").arg(v_secs_left, 0, 'f', 0)
                << qSetFieldWidth(8)
                << QString("(%L1 fps)").arg(v_fps_value, 0, 'f', 0)
                << qSetFieldWidth(8)
                << QString("(%L1%)").arg(v_pct, 0, 'f', 2)
                << qSetFieldWidth(20)
                << QString("%L1").arg(f_written_bytes)
                << qSetFieldWidth(1) << "/"
                << QString("%L1").arg(f_total_bytes)
#if 0x0
                << qSetFieldWidth(1) << "("
                << QString("[%L1]").arg(v_pct, 0, 'f', 2)
                << qSetFieldWidth(1) << "%)"
#endif
                << "\r";
    }
    void term()
    {
        fprintf(stderr, "\n");
        fflush(stdout);
    }
};

class CMain : public QObject {
    Q_OBJECT
protected:
    int f_arg_fmt;
    int f_cnt;
    QTimer f_timer;
    QNetworkAccessManager f_nam;
    QNetworkReply *f_reply;
#if 0x0
    T_Youtube_Video_Info f_video_info;
#else
    QMap<int, QUrl> f_fmt_map;
#endif
    COutput f_output;
public:
    explicit CMain()
    {
        f_cnt = 0;
        f_timer.setInterval(1000);
#if 0x0
        connect(&f_timer, SIGNAL(timeout()),
                this, SLOT(slot_timer_timeout()));
        f_timer.start();
#endif
    }
    int exec()
    {
        if(!parseArgs()) return 1;
        QStringList v_args = qApp->arguments();

        int v_result;
        v_result = _setmode( _fileno( stdout ), _O_BINARY );

#if 0x0
        QString v_token = T_Youtube_Dump_Lib::getVideoInfo(
                &f_nam, v_args.at(1), &f_video_info);
#else
        QStringList v_error_list;
        QString v_token = T_Youtube_Dump_Lib::getVideoStreamMap(
                &f_nam, v_args.at(1), f_fmt_map, v_error_list);
#endif
        qDebug() << "[v_token]" << v_token;
        //qDebug() << "[f_video_info.f_fmt_map]" << f_fmt_map;
        qDebug() << "[f_video_info.f_fmt_map.value(22)]" << f_fmt_map.value(22);
        qDebug() << "[f_video_info.f_fmt_map.value(18)]" << f_fmt_map.value(18);
        QUrl v_url = f_fmt_map.value(f_arg_fmt);
        qDebug() << "[v_url](1)" << v_url;
#if 0x0
        v_url.setEncodedUrl(v_url.toString().toLatin1());
        qDebug() << "[v_url](2)" << v_url;
#endif
        if(!v_url.isValid()) return 2;
        QUrl v_loc = v_url;
        while(v_loc.isValid())
        {
            qDebug() << "[v_loc]" << v_loc;
            QNetworkRequest v_head_request(v_loc);
            QNetworkReply *v_head_reply = f_nam.head(v_head_request);
            T_NetworkWaiter v_head_waiter(v_head_reply);
            //v_head_waiter.wait_metaDataChanged();
            v_head_waiter.wait();
            v_loc = v_head_reply->header(QNetworkRequest::LocationHeader).toUrl();
            if(v_loc.isValid()) v_url = v_loc;
            v_head_reply->deleteLater();
        }

        QNetworkRequest v_request(v_url);
        f_reply = f_nam.get(v_request);
        connect(f_reply, SIGNAL(metaDataChanged()), this, SLOT(slot_reply_metaDataChanged()));
        connect(f_reply, SIGNAL(readyRead()),  this, SLOT(slot_reply_readyRead()));
        connect(f_reply, SIGNAL(finished()),  this, SLOT(slot_reply_finished()));

        return qApp->exec();
    }
    bool parseArgs()
    {
        QStringList v_args = qApp->arguments();
        qDebug() << "[v_args]" << v_args;
        if(v_args.size() != 4) return false;
        bool v_ok;
        f_arg_fmt = v_args.at(2).toInt(&v_ok);
        return true;
    }
protected slots:
    void slot_timer_timeout()
    {
        f_cnt++;
#if 0x0
        if(f_cnt > 5)
        {
            fprintf(stderr, "\n");
            f_timer.stop();
            qApp->quit();
            return;
        }
#endif
        //fprintf(stderr, "cnt=%d   \r", f_cnt);
        fprintf(stderr, "\rcnt=%d   ", f_cnt);
    }
    void slot_reply_metaDataChanged()
    {
        QVariant v_cont_len = f_reply->header(QNetworkRequest::ContentLengthHeader);
        qDebug() << "[QNetworkRequest::HttpStatusCodeAttribute]" << f_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
        qDebug() << "[QNetworkRequest::HttpReasonPhraseAttribute]" << f_reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute);
        //qDebug() << "[QNetworkRequest::ContentLengthHeader]" << f_reply->header(QNetworkRequest::ContentLengthHeader);
        qDebug() << "[QNetworkRequest::ContentLengthHeader]" << v_cont_len;
        qDebug() << "[QNetworkRequest::ContentTypeHeader]" << f_reply->header(QNetworkRequest::ContentTypeHeader);
        qDebug() << "[QNetworkRequest::LocationHeader]" << f_reply->header(QNetworkRequest::LocationHeader);
        QUrl v_loc = f_reply->header(QNetworkRequest::LocationHeader).toUrl();
        //f_output.init(f_reply->header(QNetworkRequest::ContentLengthHeader).toLongLong());
        qDebug() << "[v_cont_len.toLongLong()]" << v_cont_len.toLongLong();
        f_output.init(v_cont_len.toLongLong());
    }

    void slot_reply_readyRead()
    {
        //qDebug() << f_reply->readAll().size();
        QByteArray v_bytes = f_reply->readAll();
        f_output.write_bytes(v_bytes);
    }
    void slot_reply_finished()
    {
        f_output.term();
        qApp->quit();
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
    QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));
    QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
#endif
    QCoreApplication::setOrganizationDomain("akio.miyoshi");
    QCoreApplication::setOrganizationName("akio.miyoshi");
    QCoreApplication::setApplicationName("yt_stream");

    CMain v_main;
    return v_main.exec();
}

#include "main.moc"
